//! Type parser for NRC language

use super::Parser;
use crate::ast::types::*;
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;

/// Parse a type
pub fn parse_type(parser: &mut Parser) -> Result<Option<Type>> {
    parse_union_type(parser)
}

/// Parse union type (A | B | C)
fn parse_union_type(parser: &mut Parser) -> Result<Option<Type>> {
    let mut types = Vec::new();

    if let Some(ty) = parse_optional_type(parser)? {
        types.push(ty);

        while parser.consume(&Token::Pipe)? {
            if let Some(ty) = parse_optional_type(parser)? {
                types.push(ty);
            } else {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected type after '|'",
                ));
            }
        }
    }

    if types.is_empty() {
        Ok(None)
    } else if types.len() == 1 {
        Ok(Some(types.into_iter().next().unwrap()))
    } else {
        Ok(Some(Type::Union(UnionType {
            member_types: types,
            location: parser.current_location(),
        })))
    }
}

/// Parse optional type (T?)
/// Syntax: T? is sugar for Option<T>
fn parse_optional_type(parser: &mut Parser) -> Result<Option<Type>> {
    let ty = parse_error_type(parser)?;

    if let Some(ty) = ty {
        if parser.consume(&Token::Question)? {
            // T? is syntactic sugar for Option<T>
            Ok(Some(Type::Option(OptionType {
                some_type: Box::new(ty),
                location: parser.current_location(),
            })))
        } else {
            Ok(Some(ty))
        }
    } else {
        Ok(None)
    }
}

/// Parse error type (T! or T!E)
/// Syntax:
/// - T! is sugar for Result<T, String>
/// - T!E is sugar for Result<T, E>
fn parse_error_type(parser: &mut Parser) -> Result<Option<Type>> {
    let ty = parse_function_type(parser)?;

    if let Some(ty) = ty {
        if parser.consume(&Token::Not)? {
            // Check if there's an error type specified (T!E syntax)
            let err_type = if let Some(err_ty) = parse_function_type(parser)? {
                // T!E -> Result<T, E>
                err_ty
            } else {
                // T! -> Result<T, String> (default error type)
                Type::Basic(BasicType::String)
            };

            Ok(Some(Type::Result(ResultType {
                ok_type: Box::new(ty),
                err_type: Box::new(err_type),
                location: parser.current_location(),
            })))
        } else {
            Ok(Some(ty))
        }
    } else {
        Ok(None)
    }
}

/// Parse function type (fn(T1, T2) -> T3)
fn parse_function_type(parser: &mut Parser) -> Result<Option<Type>> {
    if parser.consume(&Token::Fn)? {
        parser.expect(&Token::LeftParen)?;

        let mut parameter_types = Vec::new();

        if !parser.check(&Token::RightParen) {
            loop {
                if let Some(param_type) = parse_type(parser)? {
                    parameter_types.push(param_type);
                }

                if !parser.consume(&Token::Comma)? {
                    break;
                }
            }
        }

        parser.expect(&Token::RightParen)?;

        // NRC syntax: func(T1, T2) T3 (no arrow)
        // Check if there's a return type directly after the closing paren
        // Also check if we're at end of input
        let return_type = if !parser.is_at_end()
            && !parser.check(&Token::LeftBrace)
            && !parser.check(&Token::Semicolon)
            && !parser.check(&Token::Comma)
            && !parser.check(&Token::RightParen)
            && !parser.check(&Token::RightBracket)
            && !parser.check(&Token::RightBrace)
        {
            parse_type(parser)?
        } else {
            None
        };

        Ok(Some(Type::Function(FunctionType {
            parameter_types,
            return_type: return_type.map(Box::new),
            variadic: false, // TODO: Support variadic functions
            location: parser.current_location(),
        })))
    } else {
        parse_array_type(parser)
    }
}

/// Parse array type
/// Fixed-size array syntax:
/// - [10]int: fixed-size array
fn parse_array_type(parser: &mut Parser) -> Result<Option<Type>> {
    if parser.consume(&Token::LeftBracket)? {
        if let Some(Token::Integer(n)) = parser.peek().map(|t| &t.token) {
            // It's a fixed-size array: [N]T
            let size = *n as usize;
            parser.advance()?; // consume integer
            parser.expect(&Token::RightBracket)?;

            let element_type = parse_type(parser)?;
            if element_type.is_none() {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected element type after array size",
                ));
            }

            Ok(Some(Type::Array(ArrayType {
                element_type: Box::new(element_type.unwrap()),
                size: Some(size),
                location: parser.current_location(),
            })))
        } else {
            // Error: expected integer for array size
            Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected array size",
            ))
        }
    } else {
        parse_map_type(parser)
    }
}

/// Parse map type (Map<K, V>)
fn parse_map_type(parser: &mut Parser) -> Result<Option<Type>> {
    if parser.consume(&Token::MapFull)? {
        parser.expect(&Token::Less)?;

        let key_type = parse_type(parser)?;
        if key_type.is_none() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected key type in map type",
            ));
        }

        parser.expect(&Token::Comma)?;

        let value_type = parse_type(parser)?;
        if value_type.is_none() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected value type in map type",
            ));
        }

        parser.expect(&Token::Greater)?;

        Ok(Some(Type::Map(MapType {
            key_type: Box::new(key_type.unwrap()),
            value_type: Box::new(value_type.unwrap()),
            location: parser.current_location(),
        })))
    } else {
        parse_channel_type(parser)
    }
}

/// Parse channel type (chan T, chan<- T, <-chan T)
fn parse_channel_type(parser: &mut Parser) -> Result<Option<Type>> {
    if parser.consume(&Token::Chan)? {
        let direction = if parser.consume(&Token::LeftShift)? {
            ChannelDirection::Receive
        } else if parser.consume(&Token::Minus)? && parser.consume(&Token::Greater)? {
            ChannelDirection::Send
        } else {
            ChannelDirection::Bidirectional
        };

        let element_type = parse_type(parser)?;
        if element_type.is_none() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected element type in channel type",
            ));
        }

        // Parse buffer size if present
        let buffer_size = if parser.consume(&Token::LeftBracket)? {
            if let Some(Token::Integer(n)) = parser.peek().map(|t| &t.token) {
                let n = *n;
                parser.advance()?;
                parser.expect(&Token::RightBracket)?;
                Some(n as usize)
            } else {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected buffer size in channel type",
                ));
            }
        } else {
            None
        };

        Ok(Some(Type::Channel(ChannelType {
            element_type: Box::new(element_type.unwrap()),
            direction,
            buffer_size,
            location: parser.current_location(),
        })))
    } else {
        parse_reference_type(parser)
    }
}

/// Parse reference type (&T or *T) - NRC style syntax
fn parse_reference_type(parser: &mut Parser) -> Result<Option<Type>> {
    // Check if there's a '&' at the beginning (reference type)
    if parser.consume(&Token::Ampersand)? {
        // Parse the base type after the '&'
        // We need to recursively call the full type parsing chain to support &[]T, &map<K,V>, etc.
        // But we call parse_array_type to avoid infinite recursion with reference_type itself
        let base_type = parse_array_type(parser)?;

        if let Some(base_type) = base_type {
            Ok(Some(Type::Reference(ReferenceType {
                referenced_type: Box::new(base_type),
                location: parser.current_location(),
            })))
        } else {
            Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected type after '&' in reference type",
            ))
        }
    }
    // Check if there's a '*' at the beginning (pointer type, Go-style syntax)
    else if parser.consume(&Token::Star)? {
        // Parse the base type after the '*'
        // We need to recursively call the full type parsing chain to support *[]T, *map<K,V>, etc.
        // But we call parse_array_type to avoid infinite recursion with reference_type itself
        let base_type = parse_array_type(parser)?;

        if let Some(base_type) = base_type {
            Ok(Some(Type::Reference(ReferenceType {
                referenced_type: Box::new(base_type),
                location: parser.current_location(),
            })))
        } else {
            Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected type after '*' in pointer type",
            ))
        }
    } else {
        // No '&' or '*', try to parse as tuple type
        parse_tuple_type_no_pointer(parser)
    }
}

/// Parse parenthesized type (only single types allowed, no tuples)
fn parse_tuple_type_no_pointer(parser: &mut Parser) -> Result<Option<Type>> {
    if parser.consume(&Token::LeftParen)? {
        let mut element_types = Vec::new();

        if !parser.check(&Token::RightParen) {
            loop {
                if let Some(element_type) = parse_type(parser)? {
                    element_types.push(element_type);
                }

                if !parser.consume(&Token::Comma)? {
                    break;
                }
            }
        }

        parser.expect(&Token::RightParen)?;

        if element_types.is_empty() {
            Ok(Some(Type::Basic(BasicType::Void)))
        } else if element_types.len() == 1 {
            Ok(Some(element_types.into_iter().next().unwrap()))
        } else {
            // Reject tuple syntax - only single return values allowed
            Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Tuple types are not supported. Functions can only return single values.",
            ))
        }
    } else {
        parse_primary_type(parser)
    }
}

/// Parse primary type (basic types, identifiers, etc.)
fn parse_primary_type(parser: &mut Parser) -> Result<Option<Type>> {
    if parser.is_at_end() {
        return Ok(None);
    }

    match parser.peek() {
        Some(token) => match &token.token {
            // Basic types
            Token::Int => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Int)))
            }
            Token::Int8 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Int8)))
            }
            Token::Int16 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Int16)))
            }
            Token::Int32 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Int32)))
            }
            Token::Int64 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Int64)))
            }
            Token::Uint => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Uint)))
            }
            Token::Uint8 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Uint8)))
            }
            Token::Uint16 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Uint16)))
            }
            Token::Uint32 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Uint32)))
            }
            Token::Uint64 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Uint64)))
            }
            Token::Float32 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Float32)))
            }
            Token::Float64 => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Float64)))
            }
            Token::Bool => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Bool)))
            }
            Token::Rune => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Rune)))
            }
            Token::StringType => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::String)))
            }
            Token::Char(_) => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Char)))
            }
            Token::Any => {
                parser.advance()?;
                Ok(Some(Type::Basic(BasicType::Any)))
            }
            Token::Rc => {
                parser.advance()?;
                parser.expect(&Token::Less)?;
                let inner_type = parse_type(parser)?.ok_or_else(|| {
                    CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected type argument for Rc<T>",
                    )
                })?;
                parser.expect(&Token::Greater)?;
                Ok(Some(Type::Rc(RcType {
                    inner_type: Box::new(inner_type),
                    location: parser.current_location(),
                })))
            }
            Token::VecFull => {
                parser.advance()?;
                parser.expect(&Token::Less)?;
                let element_type = parse_type(parser)?.ok_or_else(|| {
                    CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected type argument for Vec<T>",
                    )
                })?;
                parser.expect(&Token::Greater)?;
                Ok(Some(Type::Vec(VecType {
                    element_type: Box::new(element_type),
                    location: parser.current_location(),
                })))
            }
            Token::MapFull => {
                parser.advance()?;
                parser.expect(&Token::Less)?;

                let key_type = parse_type(parser)?.ok_or_else(|| {
                    CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected key type for Map<K, V>",
                    )
                })?;

                parser.expect(&Token::Comma)?;

                let value_type = parse_type(parser)?.ok_or_else(|| {
                    CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected value type for Map<K, V>",
                    )
                })?;

                parser.expect(&Token::Greater)?;

                Ok(Some(Type::Map(MapType {
                    key_type: Box::new(key_type),
                    value_type: Box::new(value_type),
                    location: parser.current_location(),
                })))
            }
            Token::Weak => {
                parser.advance()?;
                parser.expect(&Token::Less)?;
                let inner_type = parse_type(parser)?.ok_or_else(|| {
                    CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected type argument for Weak<T>",
                    )
                })?;
                parser.expect(&Token::Greater)?;
                Ok(Some(Type::Weak(WeakType {
                    inner_type: Box::new(inner_type),
                    location: parser.current_location(),
                })))
            }

            // Handle other identifiers
            Token::Identifier(name) => {
                let name = name.clone();
                parser.advance()?;

                // Check for type arguments
                let type_args = if parser.consume(&Token::Less)? {
                    let mut args = Vec::new();

                    if !parser.check(&Token::Greater) {
                        loop {
                            if let Some(arg_type) = parse_type(parser)? {
                                args.push(arg_type);
                            }

                            if !parser.consume(&Token::Comma)? {
                                break;
                            }
                        }
                    }

                    parser.expect(&Token::Greater)?;
                    args
                } else {
                    vec![]
                };

                // Handle built-in Result type: Result<T, E>
                if name == "Result" && type_args.len() == 2 {
                    Ok(Some(Type::Result(ResultType {
                        ok_type: Box::new(type_args[0].clone()),
                        err_type: Box::new(type_args[1].clone()),
                        location: parser.current_location(),
                    })))
                }
                // Handle built-in Option type: Option<T>
                else if name == "Option" && type_args.len() == 1 {
                    Ok(Some(Type::Option(OptionType {
                        some_type: Box::new(type_args[0].clone()),
                        location: parser.current_location(),
                    })))
                }
                // Check if it's a struct type
                else if parser.check(&Token::LeftBrace) {
                    Ok(Some(Type::Struct(StructType {
                        name: name.clone(),
                        type_args,
                        location: parser.current_location(),
                    })))
                } else {
                    // It's either a generic type parameter or a type alias
                    if type_args.is_empty() {
                        // Check if it's a basic type name (string, bool, int32, etc.)
                        // First check if this is a basic type identifier
                        if let Some(basic_type) = name_to_basic_type(&name) {
                            return Ok(Some(Type::Basic(basic_type)));
                        }
                        // Check if it's a known generic parameter
                        // For now, treat all identifiers as generic type parameters
                        Ok(Some(Type::Generic(name.clone())))
                    } else {
                        Ok(Some(Type::Alias(AliasType {
                            name: name.clone(),
                            type_args,
                            location: parser.current_location(),
                        })))
                    }
                }
            }

            _ => Ok(None),
        },
        None => Ok(None),
    }
}

/// Convert a type name string to BasicType enum
fn name_to_basic_type(name: &str) -> Option<BasicType> {
    match name {
        "int" => Some(BasicType::Int),
        "int8" => Some(BasicType::Int8),
        "int16" => Some(BasicType::Int16),
        "int32" => Some(BasicType::Int32),
        "int64" => Some(BasicType::Int64),
        "uint" => Some(BasicType::Uint),
        "uint8" => Some(BasicType::Uint8),
        "uint16" => Some(BasicType::Uint16),
        "uint32" => Some(BasicType::Uint32),
        "uint64" => Some(BasicType::Uint64),
        "float32" => Some(BasicType::Float32),
        "float64" => Some(BasicType::Float64),
        "bool" => Some(BasicType::Bool),
        "string" | "String" => Some(BasicType::String), // Support both lowercase and uppercase
        "char" => Some(BasicType::Char),
        "rune" => Some(BasicType::Rune),
        "any" => Some(BasicType::Any),
        "void" => Some(BasicType::Void),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::lexer::Lexer;

    #[test]
    fn test_parse_basic_type() {
        let source = "int".to_string();
        let _lexer = Lexer::new(source.clone());
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        assert!(matches!(ty, Some(Type::Basic(BasicType::Int))));
    }

    #[test]
    fn test_parse_reference_type() {
        let source = "&int".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        assert!(matches!(ty, Some(Type::Reference(_))));
        if let Some(Type::Reference(ref_ty)) = ty {
            assert!(matches!(
                *ref_ty.referenced_type,
                Type::Basic(BasicType::Int)
            ));
        }
    }

    #[test]
    fn test_parse_pointer_type() {
        // NRC supports *T syntax (like Go), not T* syntax (like C++)
        let source = "*int".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        assert!(matches!(ty, Some(Type::Reference(_))));
    }

    #[test]
    fn test_parse_array_type() {
        let source = "[10]int".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        assert!(matches!(ty, Some(Type::Array(_))));
    }

    #[test]
    fn test_parse_function_type() {
        // NRC uses "func" keyword, not "fn"
        // Use StringType token instead of "string" identifier
        let source = "func(int, String) bool".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        match &ty {
            Some(Type::Function(_)) => {}
            _ => panic!("Expected Function type, got {:?}", ty),
        }
    }

    #[test]
    fn test_parse_optional_type() {
        let source = "int?".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        // int? should now be Option<int>
        assert!(matches!(ty, Some(Type::Option(_))));
        if let Some(Type::Option(opt_ty)) = ty {
            assert!(matches!(*opt_ty.some_type, Type::Basic(BasicType::Int)));
        }
    }

    #[test]
    fn test_parse_error_type_default() {
        let source = "int!".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        // int! should be Result<int, String>
        assert!(matches!(ty, Some(Type::Result(_))));
        if let Some(Type::Result(result_ty)) = ty {
            assert!(matches!(*result_ty.ok_type, Type::Basic(BasicType::Int)));
            assert!(matches!(
                *result_ty.err_type,
                Type::Basic(BasicType::String)
            ));
        }
    }

    #[test]
    fn test_parse_error_type_custom() {
        let source = "int!bool".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        // int!bool should be Result<int, bool>
        assert!(matches!(ty, Some(Type::Result(_))));
        if let Some(Type::Result(result_ty)) = ty {
            assert!(matches!(*result_ty.ok_type, Type::Basic(BasicType::Int)));
            assert!(matches!(*result_ty.err_type, Type::Basic(BasicType::Bool)));
        }
    }

    #[test]
    fn test_parse_union_type() {
        let source = "int | string | bool".to_string();
        let mut parser = Parser::new(source, None);
        parser.advance().unwrap();

        let ty = parse_type(&mut parser).unwrap();
        assert!(matches!(ty, Some(Type::Union(_))));
    }
}
